﻿// -------------------------------------------------------------------------
//    @FileName         :    NFCityMapInst.cpp
//    @Author           :    Gao.Yi
//    @Date             :    2025-09-17
//    @Email			:    445267987@qq.com
//    @Module           :    NFCityMapInst
//    @Desc             :
//
// -------------------------------------------------------------------------

#include "NFCityMapInst.h"

#include "NFCityMapGrid.h"
#include "NFCityMapMgr.h"
#include "NFCityMapUtil.h"
#include "NFCityNeighborGrid.h"

NFCityMapInst::NFCityMapInst()
{
    if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
    {
        CreateInit();
    }
    else
    {
        ResumeInit();
    }
}

NFCityMapInst::~NFCityMapInst()
{
}

int NFCityMapInst::CreateInit()
{
    m_iMapDescId = 0;
    m_ullDungeonId = 0;
    m_iMapXGridNum = 0;
    m_iMapZGridNum = 0;
    m_iMaxGridNum = 0;
    m_iMapGridSize = 0;
    m_iEnterDirection = 0;

    for (int i = 0; i < MAX_MAP_GRID_NUM; i++)
    {
        m_aiMapGrid[i] = INVALID_ID;
    }

    m_ullCreateTime = 0;
    m_iPlayerCnt = 0;

    SetTimer(1000, 0, 0, 0, 0, 0);
    return 0;
}

int NFCityMapInst::ResumeInit()
{
    return 0;
}

int NFCityMapInst::Init(int iMapDescId)
{
    int iRetCode = InitMapGrid(iMapDescId);
    CHECK_ERR(0, iRetCode, "InitMapGrid Failed [MapDescID:{}]", iMapDescId);

    iRetCode = InitMapNpc();
    CHECK_ERR(0, iRetCode, "InitMapNpc Failed [MapDescID:{}]", iMapDescId);

    m_ullCreateTime = NF_ADJUST_TIMENOW();

    return 0;
}

int NFCityMapInst::OnTimer(int timeId, int callCount)
{
    Tick();
    return 0;
}

int NFCityMapInst::Tick()
{
    m_oNpcMgr.Tick();
    if (IsNeedDestroy())
    {
        NFCityMapMgr::Instance()->DestroyMapInst(this);
    }
    return 0;
}

int NFCityMapInst::Fin()
{
    for (int i = 0; i < m_iMaxGridNum; ++i)
    {
        NFCityMapGrid* pMapGrid = NFCityMapGrid::GetObjByGlobalId(m_aiMapGrid[i]);
        if (pMapGrid)
        {
            pMapGrid->Fin();

            NFCityMapGrid::DestroyObj(pMapGrid);
        }
    }
    return 0;
}

bool NFCityMapInst::IsNeedDestroy()
{
    auto pCityMapDesc = CityMapDesc::Instance()->GetDesc(m_iMapDescId);
    if (pCityMapDesc)
    {
        if (pCityMapDesc->map_type == proto_ff::EN_MAP_TYPE_GUILD_STATION)
        {
            if (m_iPlayerCnt == 0)
            {
                return true;
            }
        }
        else if (pCityMapDesc->map_type == proto_ff::EN_MAP_TYPE_FAMILY_STATION || pCityMapDesc->map_type == proto_ff::EN_MAP_TYPE_DUNGEON)
        {
            if (m_iPlayerCnt == 0)
            {
                return true;
            }
        }
        else if (pCityMapDesc->map_type == proto_ff::EN_MAP_TYPE_GUILD_BATTLE)
        {
            uint64_t ullTimeNow = NF_ADJUST_TIMENOW();
            if (m_ullCreateTime > 0 && ullTimeNow > m_ullCreateTime + GUILD_BATTLE_DESTORY_TIME_OUT)
            {
                return true;
            }
        }
        else if (pCityMapDesc->map_type == proto_ff::EN_MAP_TYPE_RAID_STANDBY)
        {
            if (m_iPlayerCnt == 0)
            {
                return true;
            }
        }
    }

    return false;
}

int NFCityMapInst::InitMapGrid(int iMapDescID)
{
    auto pCityMapData = CityDescEx::Instance()->GetCityMapData(iMapDescID);
    CHECK_NULL_WF(0, pCityMapData, "GetCityMapData Failed [MapDescID:{}]", iMapDescID);

    m_iMapDescId = iMapDescID;
    m_iMapGridSize = pCityMapData->GetGridSize();
    m_iMapXGridNum = pCityMapData->GetXGridNum();
    m_iMapZGridNum = pCityMapData->GetZGridNum();

    auto pMapDataDesc = pCityMapData->GetMapData();
    CHECK_NULL(0, pMapDataDesc);

    m_oMapBeginPos.x = pMapDataDesc->min.x - DEFAULT_MAP_SIZE_GAP;
    m_oMapBeginPos.y = pMapDataDesc->min.y;
    m_oMapBeginPos.z = pMapDataDesc->min.z - DEFAULT_MAP_SIZE_GAP;

    m_oMapEndPos.x = pMapDataDesc->max.x + DEFAULT_MAP_SIZE_GAP;
    m_oMapEndPos.y = pMapDataDesc->max.y;
    m_oMapEndPos.z = pMapDataDesc->max.z + DEFAULT_MAP_SIZE_GAP;

    m_oMapEnterPos.x = pMapDataDesc->hero_spawn_list[0].x;
    m_oMapEnterPos.y = pMapDataDesc->hero_spawn_list[0].y;
    m_oMapEnterPos.z = pMapDataDesc->hero_spawn_list[0].z;

    if (!IsPositionValid(m_oMapEnterPos))
    {
        LOG_ERR(0, -1, "Map Enter Position Invalid, MapID:{}, Pos:({},{},{})", iMapDescID, m_oMapEnterPos.x, m_oMapEnterPos.y, m_oMapEnterPos.z);
        return -1;
    }

    m_iEnterDirection = pMapDataDesc->hero_spawn_list[0].direction;

    m_iMaxGridNum = m_iMapXGridNum * m_iMapZGridNum;

    if (m_iMaxGridNum > MAX_MAP_GRID_NUM)
    {
        CHECK_ERR(0, -1, "Map Grid Num Exceed Max Limit, MapID:{}, GridNum:{}, MaxGridNum:{}", iMapDescID, m_iMaxGridNum, MAX_MAP_GRID_NUM);
    }

    for (int i = 0; i < m_iMaxGridNum && i < MAX_MAP_GRID_NUM; ++i)
    {
        NFCityMapGrid* pMapGrid = NFCityMapGrid::CreateObj();
        CHECK_NULL_WF(0, pMapGrid, "Create MapGrid Failed, MapID:{}, GridIndex:{}", iMapDescID, i);

        pMapGrid->Init(i);

        m_aiMapGrid[i] = pMapGrid->GetGlobalId();
    }

    LOG_INFO(0, "InitMapGrid Success [MapDescID:{} GridSize:{} XGridNum:{} ZGridNum:{} MaxGridNum:{} BeginPos:({},{},{}) EndPos:({},{},{}) EnterPos:({},{},{}) EnterDir:{}]", iMapDescID, m_iMapGridSize, m_iMapXGridNum, m_iMapZGridNum, m_iMaxGridNum, m_oMapBeginPos.x, m_oMapBeginPos.y,
             m_oMapBeginPos.z, m_oMapEndPos.x, m_oMapEndPos.y, m_oMapEndPos.z, m_oMapEnterPos.x, m_oMapEnterPos.y, m_oMapEnterPos.z, m_iEnterDirection);
    return 0;
}

bool NFCityMapInst::IsPositionValid(const Proto_Vector3& position)
{
    if (position.x >= m_oMapBeginPos.x && position.x <= m_oMapEndPos.x)
    {
        if (position.z >= m_oMapBeginPos.z && position.z <= m_oMapEndPos.z)
        {
            return true;
        }
    }

    return false;
}

int NFCityMapInst::InitMapNpc()
{
    return m_oNpcMgr.Init(this);
}

int NFCityMapInst::MovePos(NFCityWorldObject* pWorldObj, const Proto_Vector3& position, int iDirection)
{
    CHECK_NULL(GetGlobalId(), pWorldObj);
    CHECK_EXPR(pWorldObj->GetMapInstId() == GetGlobalId(), -1, "world object global id:{} not in map", pWorldObj->GetGlobalId());
    int iGridObjId = GetGridObjId(position);
    auto pMapGrid = NFCityMapGrid::GetObjByGlobalId(iGridObjId);
    CHECK_NULL_WF(GetGlobalId(), pMapGrid, "Get MapGrid Failed, MapID:{}, Pos:({},{},{})", m_iMapDescId, position.x, position.y, position.z);

    int iOldGridId = pWorldObj->GetMapGridId();
    if (iGridObjId == iOldGridId)
    {
        pWorldObj->MovePos(position, iDirection);
    }
    else
    {
        NFCityNeighborGrid oBeginGrids;
        GetNeighborGrids(pWorldObj, oBeginGrids);

        LeaveMap(pWorldObj);
        EnterMap(pWorldObj, position, iDirection);

        NFCityNeighborGrid oEndGrids;
        GetNeighborGrids(pWorldObj, oEndGrids);

        NFCityMapUtil::RefreshNeighborView(pWorldObj, oBeginGrids, oEndGrids);
    }
    return 0;
}

int NFCityMapInst::EnterMap(NFCityWorldObject* pWorldObj, const Proto_Vector3& position, int iDirection)
{
    CHECK_NULL(GetGlobalId(), pWorldObj);

    int iGridObjIdx = GetGridObjId(position);

    auto pMapGrid = NFCityMapGrid::GetObjByGlobalId(iGridObjIdx);
    CHECK_NULL_WF(GetGlobalId(), pMapGrid, "Get MapGrid Failed, MapID:{}, Pos:({},{},{})", m_iMapDescId, position.x, position.y, position.z);

    pWorldObj->OnEnterMap(GetGlobalId(), m_iMapDescId);

    pMapGrid->EnterGrid(pWorldObj, position, iDirection);

    if (pWorldObj->IsPlayer())
    {
        ++m_iPlayerCnt;
    }

    return 0;
}

int NFCityMapInst::LeaveMap(NFCityWorldObject* pWorldObj)
{
    CHECK_NULL(GetGlobalId(), pWorldObj);
    CHECK_EXPR(pWorldObj->GetMapInstId() == GetGlobalId(), -1, "world object global id:{} not in map", pWorldObj->GetGlobalId());

    int iMapGridIdxInMap = pWorldObj->GetMapGridIdx();
    if (!IsMapGridIndexValid(iMapGridIdxInMap))
    {
        LOG_ERR(GetGlobalId(), -1, "WorldObj:{} grid index: in map invalid!", pWorldObj->GetGlobalId(), iMapGridIdxInMap);
        return -1;
    }

    NFCityMapGrid* pMapGrid = NFCityMapGrid::GetObjByGlobalId(m_aiMapGrid[iMapGridIdxInMap]);
    CHECK_NULL(GetGlobalId(), pMapGrid);

    pMapGrid->LeaveGrid(pWorldObj);

    pWorldObj->OnLeaveMap();

    if (pWorldObj->IsPlayer())
    {
        --m_iPlayerCnt;
    }

    return 0;
}

int NFCityMapInst::GetGridObjId(const Proto_Vector3& position)
{
    CHECK_EXPR(IsPositionValid(position), -1, "pos(x:{} y:{}) is valid in map:{}", position.x, position.y, m_iMapDescId);

    int iXGrid = (position.x - m_oMapBeginPos.x) / m_iMapGridSize;
    int iZGrid = (position.z - m_oMapBeginPos.z) / m_iMapGridSize;

    int iGridIndex = iZGrid * m_iMapXGridNum + iXGrid;
    CHECK_EXPR(iGridIndex >= 0 && iGridIndex < MAX_MAP_GRID_NUM, -1, "cal grid index failed, pos(x:{} y:{}), mapid:{}", position.x, position.y, m_iMapDescId);

    return m_aiMapGrid[iGridIndex];
}

int NFCityMapInst::GetMapGridId(int iGridIdx)
{
    if (IsMapGridIndexValid(iGridIdx))
    {
        return m_aiMapGrid[iGridIdx];
    }

    return INVALID_ID;
}

int NFCityMapInst::EnterMapBornPos(NFCityWorldObject* pWorldObj)
{
    CHECK_NULL(GetGlobalId(), pWorldObj);

    return EnterMap(pWorldObj, m_oMapEnterPos, m_iEnterDirection);
}

void NFCityMapInst::GetEnterPos(Proto_Vector3& oEnterPos, int& iDirection)
{
    int iX = m_oMapEnterPos.x;
    int iZ = m_oMapEnterPos.z;

    oEnterPos.x = iX;
    oEnterPos.y = m_oMapEnterPos.y;
    oEnterPos.z = iZ;

    iDirection = m_iEnterDirection;
}

int NFCityMapInst::IteratorWorldObject(NFICityWorldObjectFunc* pFunc, ECityWorldListType eListType)
{
    CHECK_NULL(GetGlobalId(), pFunc);

    NFCityMapGrid* pMapGrid = nullptr;
    for (int i = 0; i < m_iMaxGridNum && i < (int)ARRAYSIZE(m_aiMapGrid); ++i)
    {
        pMapGrid = NFCityMapGrid::GetObjByGlobalId(m_aiMapGrid[i]);
        if (pMapGrid)
        {
            pMapGrid->IteratorWorldObject(pFunc, eListType);
        }
    }

    return 0;
}

int NFCityMapInst::GetNeighborGrids(NFCityWorldObject* pWorldObj, NFCityNeighborGrid& oGrids)
{
    CHECK_NULL(GetGlobalId(), pWorldObj);

    Proto_Vector3* pPos = pWorldObj->GetPosition();

    int iXGridNum = (pPos->x - m_oMapBeginPos.x) / m_iMapGridSize;
    int iZGridNum = (pPos->z - m_oMapBeginPos.z) / m_iMapGridSize;

    int iXGridBegin = iXGridNum - 1;
    int iXGridEnd = iXGridNum + 1;
    iXGridBegin = std::max(0, iXGridBegin);
    iXGridEnd = std::min(iXGridEnd, m_iMapXGridNum - 1);

    int iZGridBegin = iZGridNum - 1;
    int iZGridEnd = iZGridNum + 1;
    iZGridBegin = std::max(0, iZGridBegin);
    iZGridEnd = std::min(iZGridEnd, m_iMapZGridNum - 1);

    for (int iX = iXGridBegin; iX <= iXGridEnd; ++iX)
    {
        for (int iZ = iZGridBegin; iZ <= iZGridEnd; ++iZ)
        {
            int iGridIndex = iZ * m_iMapXGridNum + iX;
            CHECK_EXPR(IsMapGridIndexValid(iGridIndex), -1, "cal grid index failed, pos(x:{} y:{}), mapid:{}", pPos->x, pPos->y, m_iMapDescId);
            oGrids.AddGrid(m_aiMapGrid[iGridIndex]);
        }
    }

    return 0;
}
